// 一. 将下列异步代码使用promise的方式改进
// (1)
let promise1 = new Promise((resolve, reject) => {
  resolve('hello')
})
promise1.then(res => res + ' lagou').then(res => console.log(res + ' I ❤️ U'))

// 二. 基于以下代码完成下面四个练习
const fp = require('lodash/fp')
const {cars} = require('./data.js')

// 1 使用函数组合获取最后一条数据的stock属性
const lastStock = fp.flowRight(fp.prop('in_stock'), fp.last)
console.log(lastStock(cars)) // false

// 2 使用fp.flowRight(), fp.prop(), fp.first获取第一个car的name
const firstCarName = fp.flowRight(fp.prop('name'), fp.first)
console.log(firstCarName(cars)) // Ferrari FF

// 3 使用帮助函数_average重构averageDollarValue, 使用函数组合方式实现
let _average = function(xs) {
  return fp.reduce(fp.add, 0, xs) / xs.length
}
let averageDollorValue = function(cars) {
  // let result = fp.flowRight(_average, fp.map((car => car.dollar_value), cars)) 
  let result = fp.flowRight(_average, fp.map((car => car.dollar_value))) 
  return result(cars)
}
console.log(averageDollorValue(cars));

// 4. 使用flowRight写一个sanitizeNames()函数, 返回一个下划线连接的小写字符串, 把数组中的name转换成这种形式, 例如: sanitizeName(["Hello World"]) => ["hello_world"]
let array = ["Hello World"]
let _underscore = fp.replace(/\W+/g, '_')
// function sanitizeNames(array) {
//   let a = fp.flowRight(_underscore, fp.map(item => fp.toLower(item)))
//   return a(array)
// }
let sanitizeNames2 = fp.flowRight(_underscore, fp.map(item => fp.toLower(item)))
console.log(sanitizeNames2(array))


// 三. 基于support.js提供的代码, 完成后续四个练习

// 1. 使用fp.add(x, y) 和 fp.map(f, x)创建一个能让functor里的值增加的函数ex1
const {Maybe, Container} = require('./support')
let maybe = Maybe.of([5, 6, 1])
let ex1 = val => {
  return maybe.map(item => {
    return fp.map(x => fp.add(x, val), item)
  })
}
// console.log(ex1(1));

// 2. 实现一个函数ex2, 能够使用fp.first获取列表的第一个元素
let xs = Container.of(['do', 're', 'mi', 'fa', 'so', 'la', 'xi', 'do'])
let ex2 = () => {
  return xs.map(fp.first)
}
console.log(ex2())

// 3. 实现一个ex3, 使用safeProp 和 fp.first找到user的名字的首字母
let safeProp = fp.curry(function(x, o) {
  return Maybe.of(o[x])
})
let user = {id: 2, name: 'Albert'}
let ex3 = () => {
  return safeProp('name', user)
  .map(fp.first)
}
console.log(ex3())

// 4. 使用Maybe重写ex4, 不要有if语句
let ex4 = function(n) {
  return Maybe.of(n).map(parseInt)
}
console.log(ex4('43'))

// 四: 手写实现MyPromise源码, 尽可能还原promsie中的每个API

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
  // 接收一个执行器，这个执行器有两个参数， 且执行器是立即执行的
  constructor(executor) {
    try{
      executor(this.resolve, this.reject)
    }catch(e){
      //TODO handle the exception
      this.reject(e)
    }
  }
  value = undefined // 成功参数
  reason = undefined // 失败原因
  status = PENDING // promise状态
  successCallback = []
  failCallback = []
  // resolve 成功回调函数 接收一个参数, 如果状态不是pending, 则更改promise状态为成功
  resolve = (value) => {
    if (this.status !== PENDING) return
    this.status = FULFILLED
    this.value = value
    while(this.successCallback.length) {
      this.successCallback.shift()()
    }
  }
  reject = reason => {
    if (this.status !== PENDING) return
    this.status = REJECTED
    this.reason = reason
    while(this.failCallback.length) {
      this.failCallback.shift()()
    }
  }
  // then方法 接收两个回调函数，成功回调，和失败回调，需要判断status状态，成功则执行成功回调，失败就执行失败回调
  then(successCallback, failCallback) {
    // then方法变为可选参数
    successCallback = successCallback ? successCallback : value => value
    failCallback = failCallback ? failCallback : reason => {throw reason}
    // 链式调用then then 需要返回一个promise对象
    let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => { // 必须要用箭头函数，否则会改变this，返回值失败
          try{ // try catch捕获异常是在setTimeout函数内部捕获的
           let x = successCallback(this.value)
           resolvePromise(promise2, x, resolve, reject) 
          }catch(e){
            //TODO handle the exception
            reject(e)
          }
        }, 0)
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try{
            let x = failCallback(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          }catch(e){
            //TODO handle the exception
            reject(e)
          }
        }, 0)
      } else {
        // 处理异步pending情况
        // this.successCallback = successCallback
        // this.failCallback = failCallback
        // 处理异步情况多次调用then
        this.successCallback.push(() => {
          setTimeout(() => { // 必须要用箭头函数，否则会改变this，返回值失败
            try{ // try catch捕获异常是在setTimeout函数内部捕获的
             let x = successCallback(this.value)
             resolvePromise(promise2, x, resolve, reject) 
            }catch(e){
              //TODO handle the exception
              reject(e)
            }
          }, 0)
        })
        this.failCallback.push(() => {
          setTimeout(() => {
            try{
              let x = failCallback(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            }catch(e){
              //TODO handle the exception
              reject(e)
            }
          }, 0)
        })
      }
    })
    return promise2
  }
  // 实现all方法 是一个静态方法，接收一个数组，返回一个promise对象
  // 这个数组可以传入普通值，也可以是一个promsie对象
  // 其中一个处理失败就整体失败，抛出异常
  static all(array) {
    let result = [] // 最终的结果数组
    let index = 0 // 设置已经处理过的长度，只有全部处理完成，才返回最终值，防止异步情况
    return new MyPromise((resolve, reject) => {
      function addData(key, value) {
        result[key] = value
        index ++
        if (index === array.length) resolve(result)
      }
      for (let i = 0; i < array.length; i++) { // 需要用let 定义变量i 如果用var定义遇到promise代码会有问题？？？？？
        // 判断传入的是普通值还是一个promise对象
        var current = array[i]
        if (current instanceof MyPromise) {
          current.then(res => addData(i, res), err => reject(err))
        } else {
          addData(i, current)
        }
      }
    })
  }
  // 实现resolve方法
  // 该方法接收一个参数，可以是普通值也可以是promise对象
  // 返回一个promise对象
  static resolve(x) {
    if (x instanceof MyPromise) return x
    return new MyPromise(resolve => resolve(x))
  }
  // finally方法 返回一个promise, 且不管成功还是失败都会执行一次
  // 在finally方法后面，可以链式调用then, 来拿到当前promise对象的最终返回值
  // finally不是静态方法，需要定义在myPromise这个原型对象上
  finally(callback) {
    return this.then(value => { // 不管成功还是失败都执行一下这个callback
      MyPromise.resolve(callback) // 解析成promise对象
      .then(() => value) // 把当前value返回
    }, reason => {
      MyPromise.resolve(callback)
      .then(() => {throw reason})
    })
  }
  // catch 方法 与then方法类似，只是不需要处理成功回调，只处理失败回调即可
  catch(failCallback) {
    return this.then(undefined, failCallback)
  }
}
// 处理回调
function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) return
  if (x instanceof MyPromise) {
    x.then(res => {
      resolve(res)
    }, err => reject(err))
  } else {
    resolve(x)
  }
}